<html>
<head>

<title>Groovy Goodness: @DelegatesTo For Type Checking DSL</title>

<script language="javascript" src="scripts/shCore.js"></script> 
<script language="javascript" src="scripts/shLegacy.js"></script> 
<script language="javascript" src="scripts/shBrushJava.js"></script> 
<script language="javascript" src="scripts/shBrushXml.js"></script> 
<script language="javascript" src="scripts/shBrushJScript.js"></script> 
<script language="javascript" src="scripts/shBrushGroovy.js"></script> 
<script language="javascript" src="scripts/shBrushPlain.js"></script> 
<script language="javascript" src="scripts/shBrushBash.js"></script> 
 
<link href="styles/reset.css" rel="stylesheet" type="text/css" />
<link href="styles/shCore.css" rel="stylesheet" type="text/css" />
<link type="text/css" rel="stylesheet" href="styles/shThemeRDark.css"/>
<link href="styles/blog.css" rel="stylesheet" type="text/css" />

</head>
<body>

<a href="index.html">Back to index</a>

<h3 class="post-title">Groovy Goodness: @DelegatesTo For Type Checking DSL</h3>

<div class="post">
<p>Groovy 2.1 introduced the <code>@DelegatesTo</code> annotation. With this annotation we can document a method and tell which class is responsible for executing the code we pass into the method. If we use <code>@TypeChecked</code> or <code>@CompileStatic</code> then the static type checker of the compiler will use this information to check at <b>compile-time</b> if the code is correct. And finally this annotation allows an IDE to give extra support like code completion.</p><p>Suppose we have the following class <code>Reservation</code> with the method <code>submit()</code>. The method accepts a closure with methods that need to be applied with the instance of the <code>Reservation</code> class. This is a very common pattern for writing simple DSLs in Groovy.</p><pre class="brush:groovy">class Reservation {
    private Date date
    private String event
    private String attendee
    
    void date(final Date date) { this.date = date }
    void event(final String event) { this.event = event }
    void attendee(final String attendee) { this.attendee = attendee }
    
    /** Submit a reservation. 
      * @param config Configuration for reservation, invoking method class on Reservation.
      */
    static void submit(final Closure config) {
        final Reservation reservation = new Reservation()
        reservation.with config
    }

}

class Event {
    /** Use Reservation configuration DSL to submit a reservation. */
    void submitReservation() {
        Reservation.submit {
            date Date.parse('yyyyMMdd', '20130522')
            event 'Gr8Conf'
            attendee 'mrhaki'
            reserved true
        }
    }
}

final event = new Event()
event.submitReservation()
</pre><p>When we look at the code we might already see there is an error. In the <code>Event.submitReservation()</code> method we have the line <code>reserved true</code>, which will try to invoke the <code>reserve()</code> method of the <code>Reservation</code> class. But that method is not defined. When we run the application we get the expected error:</p><pre class="brush:plain;light:true">Exception thrown
May 28, 2013 6:58:36 AM org.codehaus.groovy.runtime.StackTraceUtils sanitize
WARNING: Sanitizing stacktrace:
groovy.lang.MissingMethodException: No signature of method: Reservation.reserved() is applicable for argument types: (java.lang.Boolean) values: [true]
</pre><p>To get an error for this line at compile-time we must add some annotation so the Groovy compiler can do static type checking on our code. We change the code and get the following sample:</p><pre class="brush:groovy;highlight:[14,21]">import groovy.transform.*

class Reservation {
    private Date date
    private String event
    private String attendee
    
    void date(final Date date) { this.date = date }
    void event(final String event) { this.event = event }
    void attendee(final String attendee) { this.attendee = attendee }
    
    /** Submit a reservation. 
      * @param config Configuration for reservation, invoking method class on Reservation.
      */
    static void submit(@DelegatesTo(Reservation) final Closure config) {
        final Reservation reservation = new Reservation()
        reservation.with config
    }

}

@TypeChecked
// @CompileStatic - will also do static type checking
class Event {
    /** Use Reservation configuration DSL to submit a reservation. */
    void submitReservation() {
        Reservation.submit {
            date Date.parse('yyyyMMdd', '20130522')
            event 'Gr8Conf'
            attendee 'mrhaki'
            reserved true
        }
    }
}

final event = new Event()
event.submitReservation()
</pre><p>When the code is compiled we immediately get a compilation error:</p><pre class="brush:plain;light:true">1 compilation error:

[Static type checking] - Cannot find matching method Event#reserved(boolean). Please check if the declared type is right and if the method exists.
 at line: 26, column: 13
</pre><p>Wow, this useful! We find errors in our DSL before the code is run.</p><p>When we create this code in an IDE like IntelliJ IDEA we also get code completion in the <code>Reservation.submit()</code> method invocation in the <code>Event</code> class. The following screenshot shows code completion and a red font for <code>reserved</code> to indicate the compilation error.</p><a href="http://3.bp.blogspot.com/-6h0S2iXtxqY/UaR7A4PXvgI/AAAAAAAAHBk/RJ2nVpNQF0g/s1600/delegates-to-idea-screenshot.png" imageanchor="1" ><img border="0" src="http://3.bp.blogspot.com/-6h0S2iXtxqY/UaR7A4PXvgI/AAAAAAAAHBk/RJ2nVpNQF0g/s320/delegates-to-idea-screenshot.png" style="display:block;text-align:center;margin:0 auto 10px"/></a><br />
<p>Code written in Groovy 2.1.3</p
</div>

<script language="javascript"> 
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf';
SyntaxHighlighter.defaults['first-line'] = 0;
SyntaxHighlighter.defaults['auto-links'] = false;
SyntaxHighlighter.all();
dp.SyntaxHighlighter.HighlightAll('code');
</script>

</body>
</html>